package com.unione.unione_network.tcp_socket

import android.os.Handler
import android.os.Looper
import android.util.Log
import com.google.gson.Gson
import com.unione.unione_network.AGVSocketManager
import com.unione.unione_network.tcp_socket.bean.MsgType
import com.unione.unione_network.tcp_socket.bean.TargetInfo
import com.unione.unione_network.tcp_socket.bean.TcpMsg
import java.io.IOException
import java.net.InetSocketAddress
import java.net.Socket
import java.net.SocketException
import java.util.Arrays
import java.util.concurrent.LinkedBlockingQueue


enum class ClientState {
    Disconnected, Connecting, Connected
}

class TcpClient(var mTargetInfo: TargetInfo, private val mTcpConnConfig: TcpConnConfig) {

    companion object {
        const val TAG = "TcpClient"
    }


    private var mClientState: ClientState = ClientState.Disconnected
    private var mConnectionThread: ConnectionThread? = null
    private var mSendThread: SendThread? = null
    private var mReceiveThread: ReceiveThread? = null
    private var msgQueue: LinkedBlockingQueue<TcpMsg> = LinkedBlockingQueue()
    private var mSocket: Socket? = null
    private var mTcpClientListeners: MutableList<TcpClientListener> = mutableListOf()
    private var mUIHandler: Handler = Handler(Looper.getMainLooper())
    private var mTcpClient: TcpClient = this

    init {
        AGVSocketManager.putTcpClient(this)
    }


    fun runOnUiThread(runnable: Runnable) {
        mUIHandler.post(runnable)
    }

    @Synchronized
    fun connect() {
        if (!isDisconnected()) {
            Log.d(TAG, "已经连接了或正在连接")
            return
        }
        Log.d(TAG, "tcp connecting")
        mClientState = ClientState.Connecting
        getConnectionThread()?.start()

    }

    fun getConnectionThread(): ConnectionThread? {
        if (mConnectionThread == null || !mConnectionThread!!.isAlive || mConnectionThread!!.isInterrupted) {
            mConnectionThread = ConnectionThread()
        }
        return mConnectionThread
    }

    fun getSendThread(): SendThread? {
        if (mSendThread == null || !mSendThread!!.isAlive) {
            mSendThread = SendThread()
        }
        return mSendThread
    }

    fun getReceiveThread(): ReceiveThread? {
        if (mReceiveThread == null || !mReceiveThread!!.isAlive) {
            mReceiveThread = ReceiveThread()
        }
        return mReceiveThread
    }

    @Synchronized
    fun sendMsg(message: String) {
        sendMsg(TcpMsg().apply {
            sourceDataString = message
        })
    }

    @Synchronized
    fun sendMsg(msg: TcpMsg) {
        if (isDisconnected()) {
            Log.d(TAG, "发送消息 $msg，当前没有tcp连接，先进行连接")
            connect()
        }
        enqueueTcpMsg(msg)
    }

    fun enqueueTcpMsg(msg: TcpMsg): Boolean {
        if (msgQueue.contains(msg)) {
            return false
        }
        try {
            msgQueue.put(msg)
            return true
        } catch (e: InterruptedException) {

        }
        return false
    }

    @Synchronized
    fun disconnect() {
        disconnect("手动关闭tcpclient", null)
    }

    @Synchronized
    fun disconnect(msg: String, e: Exception?) {
        if (isDisconnected()) return
        closeSocket()
        getConnectionThread()?.interrupt()
        getSendThread()?.interrupt()
    }

    @Synchronized
    fun getSocket(): Socket? {
        if (mSocket == null || isDisconnected() || !mSocket!!.isConnected) {
            mSocket = Socket()
            try {
                mSocket!!.soTimeout = mTcpConnConfig.receiveTimeout.toInt()
            } catch (e: SocketException) {

            }
        }
        return mSocket
    }

    @Synchronized
    fun onErrorDisConnect(msg: String, e: Exception?) {
        if (isDisconnected()) return
        disconnect(msg, e)
        if (mTcpConnConfig.isReconnect) {
            connect()
        }
    }

    private fun closeSocket() {

    }


    fun isDisconnected(): Boolean {
        return mClientState == ClientState.Disconnected
    }

    fun isConnected(): Boolean {
        return mClientState == ClientState.Connected
    }


    inner class ConnectionThread : Thread() {
        override fun run() {
            try {
                val localPort: Int = mTcpConnConfig.localPort
                if (localPort > 0) {
                    if (!getSocket()!!.isBound) {
                        getSocket()!!.bind(InetSocketAddress(localPort))
                    }
                }
                getSocket()!!.connect(
                    InetSocketAddress(mTargetInfo.ip, mTargetInfo.port),
                    mTcpConnConfig.connTimeout.toInt()
                )
                Log.d(TAG, "创建连接成功,target=$mTargetInfo,localport=$localPort")
            } catch (e: Exception) {
                Log.d(TAG, "创建连接失败,target=$mTargetInfo,$e")
                onErrorDisConnect("创建连接失败", e)
                return
            }
            notifyConnected()
            onConnectSuccess()
        }
    }

    fun notifyConnected() {
        for (l in mTcpClientListeners) {
            runOnUiThread {
                l.onConnected(mTcpClient)
            }
        }
    }

    fun notifySended(tcpMsg: TcpMsg) {
        for (l in mTcpClientListeners) {
            runOnUiThread {
                l.onSendData(mTcpClient, tcpMsg)
            }
        }
    }

    fun notifyReceive(tcpMsg: TcpMsg) {
        for (l in mTcpClientListeners) {
            runOnUiThread {
                l.onReceive(mTcpClient, tcpMsg)
            }
        }
    }

    fun notifyValidationFail(tcpMsg: TcpMsg) {
        for (l in mTcpClientListeners) {
            runOnUiThread {
                l.onValidationFail(mTcpClient, tcpMsg)
            }
        }
    }

    fun addTcpClientListener(listener: TcpClientListener) {
        if (mTcpClientListeners.contains(listener)) return
        mTcpClientListeners.add(listener)
    }

    fun removeTcpClientListener(listener: TcpClientListener) {
        mTcpClientListeners.remove(listener)
    }

    fun onConnectSuccess() {
        Log.d(TAG, "tcp connect 建立成功")
        mClientState = ClientState.Connected
        getSendThread()?.start()
        getReceiveThread()?.start()
    }

    inner class SendThread : Thread() {

        private var sendingTcpMsg: TcpMsg? = null

        fun cancel(msg: TcpMsg): Boolean {
            return msgQueue.remove(msg)
        }

        fun setSendingTcpMsg(msg: TcpMsg): SendThread {
            sendingTcpMsg = msg
            return this
        }


        override fun run() {
            var msg: TcpMsg? = null
            try {
                while (isConnected() && !interrupted() && msgQueue.take().also {
                        msg = it
                    } != null
                ) {
                    setSendingTcpMsg(msg!!)
                    Log.d(TAG, "tcp sending msg=$msg")
                    var data: ByteArray? = msg!!.sourceDataBytes
                    if (data == null) { //根据编码转换消息
                        data = msg!!.sourceDataString?.toByteArray(mTcpConnConfig.charsetName)
                    }
                    if (data != null && data.isNotEmpty()) {
                        try {
                            getSocket()!!.getOutputStream().write(data)
                            getSocket()!!.getOutputStream().flush()
                            msg!!.setTime()
                            notifySended(msg!!)
                        } catch (e: IOException) {
                            e.printStackTrace()
                            onErrorDisConnect("发送消息失败", e)
                            return
                        }
                    }
                }
            } catch (e: InterruptedException) {
//                e.printStackTrace();
            }
        }
    }

    inner class ReceiveThread : Thread() {
        override fun run() {
            try {
                val inputStream = getSocket()!!.getInputStream()
                while (isConnected() && !interrupted()) {
                    val buffer = ByteArray(1024)
                    var remainingBytes: ByteArray? = null
                    var currentMessageSize = 0
                    while (true) {
                        val bytesRead = inputStream.read(buffer)
                        if (bytesRead == -1) {
                            break
                        }
                        val totalBytes = when (remainingBytes) {
                            null -> buffer.copyOfRange(0, bytesRead)
                            else -> {
                                val combined = ByteArray(remainingBytes.size + bytesRead)
                                System.arraycopy(
                                    remainingBytes,
                                    0,
                                    combined,
                                    0,
                                    remainingBytes.size
                                )
                                System.arraycopy(
                                    buffer,
                                    0,
                                    combined,
                                    remainingBytes.size,
                                    bytesRead
                                )
                                combined
                            }
                        }
                        val sigByte = '\r'.code.toByte()
                        when (val index = totalBytes.indexOfFirst { it == sigByte }) {
                            -1 -> {
                                // 此时意味着数据太大 要等下一个buffer
                                remainingBytes = totalBytes.copyOf()
                            }

                            0 -> {
                                //两种情况/r/n 或者/r/n/r/n
                                remainingBytes = if (totalBytes[2] == sigByte) {
                                    totalBytes.copyOfRange(4, totalBytes.size)
                                } else {
                                    totalBytes.copyOfRange(2, totalBytes.size)
                                }
                            }

                            1 -> {
                                remainingBytes = totalBytes.copyOfRange(3, totalBytes.size)
                            }

                            else -> {
                                val message = totalBytes.copyOfRange(0, index)
                                val messageStr =
                                    String(message).replace("\\r", "").replace("\\n", "")
                                val tcpMsg = TcpMsg().apply {
                                    sourceDataBytes =
                                        messageStr.toByteArray(mTcpConnConfig.charsetName)
                                    target = mTargetInfo
                                    msgType = MsgType.Receive
                                    sourceDataString = messageStr
                                }
                                tcpMsg.setTime()
                              //  Log.d(TAG, "tcp Receive  succ msg= ${tcpMsg.sourceDataString}")
                                notifyReceive(tcpMsg) //notify listener
                                remainingBytes = totalBytes.copyOfRange(index, totalBytes.size)
                            }
                        }
                    }
                }
            } catch (e: java.lang.Exception) {
                Log.d(TAG, "tcp Receive  error  $e")
                onErrorDisConnect("接受消息错误", e)
            }
        }
    }

}


